import asyncio
from dataclasses import dataclass

import msgpack

from dicom_viewer_be_mock_xiaosai.config import EnumImgTransferMode
from src.dicom_viewer_be_mock_xiaosai.utils.logger import logger
from src.dicom_viewer_be_mock_xiaosai.components.ViewGroupManager import ViewGroupManager
from src.dicom_viewer_be_mock_xiaosai.type import EnumPlayState, EnumViewMode, EnumRotateSubType
from src.dicom_viewer_be_mock_xiaosai import config
from src.dicom_viewer_be_mock_xiaosai.components.CoordTransfer import CoordTransfer
from src.dicom_viewer_be_mock_xiaosai.components.Viewer import Viewer
from src.dicom_viewer_be_mock_xiaosai.components.VolumeInfo import VolumeInfo
from src.dicom_viewer_be_mock_xiaosai.loader.DicomLoader import DicomLoader
from src.dicom_viewer_be_mock_xiaosai.loader.DicomTagLoader import DicomTagLoader
from src.dicom_viewer_be_mock_xiaosai.service.socketio_service import send_message
from src.dicom_viewer_be_mock_xiaosai.type import EnumViewType, EnumImgQuality, EnumOpType
from src.dicom_viewer_be_mock_xiaosai.utils.util import debounce


@dataclass
class LoaderState:
    has_dicom = False
    stack_size_set = False
    stack_transform_init = False
    mpr_img_loaded = False

    def set_has_dicom_state(self, state):
        self.has_dicom = state

    def set_stack_size_set_state(self, state):
        self.stack_size_set = state

    def set_stack_transform_init_state(self, state):
        self.stack_transform_init = state

    def set_mpr_img_loaded_state(self, state):
        self.mpr_img_loaded = state





class ImgLoader:
    def __init__(self, group_id):
        self.group_id = group_id
        self.dicom_tag_info = DicomTagLoader()
        self.coord_transfer: CoordTransfer = CoordTransfer()

        self.volume_info = VolumeInfo(self.dicom_tag_info, self.coord_transfer)
        self.stack_viewer = Viewer(view_type=EnumViewType.stack.value,
                                   dicom_tag_info=self.dicom_tag_info,
                                   volume_info=self.volume_info, id_info={
                "group_id":group_id
            })
        self.ax_viewer = Viewer(view_type=EnumViewType.Ax.value, dicom_tag_info=self.dicom_tag_info,
                                volume_info=self.volume_info, id_info={
                "group_id":group_id
            })
        self.sag_viewer = Viewer(view_type=EnumViewType.Sag.value, dicom_tag_info=self.dicom_tag_info,
                                 volume_info=self.volume_info,id_info={
                "group_id":group_id
            })
        self.cor_viewer = Viewer(view_type=EnumViewType.Cor.value, dicom_tag_info=self.dicom_tag_info,
                                 volume_info=self.volume_info,id_info={
                "group_id":group_id
            })

        # self.volume_viewer = VolumeLoader(self.dicom_tag_info)
        self.loader_state = LoaderState()
        self.is_playing = False
        self.ds_arr = []
        # self.dicom_loader = DicomLoader(dicom_folder_path)



    def pack_http_ans(self, content):
        return {
            "msgType": "img",
            "content": content
        }

    async def load_dicom_at_once(self, series_id):
        logger.info(f'解析seriesId:{series_id},所对应的ds数据')
        self.ds_arr = DicomLoader.series_dict.get(series_id)
        self.dicom_tag_info.init_tag_info(self.ds_arr[0])
        self.volume_info.init_spacing_info()
        if self.loader_state.has_dicom is False:
            self.init_after_volume_loaded()
            self.loader_state.set_has_dicom_state(True)

    def init_after_volume_loaded(self):
        self.volume_info.init_origin_volume_by_exist_files(self.ds_arr)
        self.volume_info.init_stack_view_current_index()

    async def set_stack_size(self,view_id, size):
        logger.info('set stack size')
        self.volume_info.set_canvas_size(EnumViewType.stack.value, size)
        self.loader_state.set_stack_size_set_state(True)
        self.volume_info.update_transform(EnumViewType.stack.value)
        self.loader_state.set_stack_transform_init_state(True)
        await self.send_current_stack_img(view_id)

    def init_mpr(self):
        self.volume_info.init_norm_volume()
        self.volume_info.init_mpr_view_current_index()

    async def set_mpr_viewer_size(self, sub_view_id, view_type, size):
        logger.info(f'set mpr size,, {size}')
        if self.loader_state.mpr_img_loaded is False:
            self.coord_transfer.init(self.ds_arr)
            self.init_mpr()
        self.loader_state.set_mpr_img_loaded_state(True)
        size_info = (size['height'], size['width'])
        self.volume_info.set_canvas_size(view_type, size_info)
        self.volume_info.update_transform(view_type)
        await self.get_view(sub_view_id, view_type)

    async def get_view(self, sub_view_id, view_type):
        content = None
        match view_type:
            case EnumViewType.Ax.value | EnumViewType.Sag.value | EnumViewType.Cor.value:
                viewer_obj_arr = self.get_view_type(view_type)
                viewer = viewer_obj_arr[0]['viewer']
                content = {
                    sub_view_id: viewer.get_mix_img(EnumImgQuality.high.value)
                }
            case EnumViewType.Volume.value:
                pass
                # img_info = self.volume_viewer.get_high_quality_img()
                # ans = {
                #     EnumViewType.Volume.value: img_info
                # }
        ans = self.pack_http_ans(content)
        send_msg = self.get_transformed_msg(ans)
        await send_message(send_msg)

    async def send_current_stack_img(self, view_id ):
        if self.loader_state.stack_size_set is False or self.loader_state.has_dicom is False:
            return
        img_info = self.stack_viewer.get_recent_img(EnumImgQuality.low.value)
        content = {
            view_id: img_info
        }
        ans = self.pack_http_ans(content)
        send_msg = self.get_transformed_msg(ans)
        await send_message(send_msg)
        await self.delayed_render([{
            "view_type": EnumViewType.stack.value,
            "view_id": view_id,
            "viewer": self.stack_viewer
        }
        ])

    def get_transformed_msg(self, img_dict):
        if config.img_transfer_mode == EnumImgTransferMode.binary.value:
            return msgpack.packb(img_dict, use_bin_type=True)
        elif config.img_transfer_mode == EnumImgTransferMode.base64.value:
            return img_dict
        return None

    def get_view_type(self, view_type):
        stack = {
            "view_type": EnumViewType.stack.value,
            "viewer": self.stack_viewer
        }
        Ax = {
            "view_type": EnumViewType.Ax.value,
            "viewer": self.ax_viewer
        }
        Sag = {
            "view_type": EnumViewType.Sag.value,
            "viewer": self.sag_viewer
        }
        Cor = {
            "view_type": EnumViewType.Cor.value,
            "viewer": self.cor_viewer
        }
        MPR = [Ax, Sag, Cor]
        match view_type:
            case EnumViewType.stack.value:
                return [stack]
            case EnumViewType.Sag.value:
                return [Sag]
            case EnumViewType.Ax.value:
                return [Ax]
            case EnumViewType.Cor.value:
                return [Cor]
            case EnumViewType.MPR.value:
                return MPR
            case EnumViewType.Volume.value:
                return None
                # return [{
                #     "view_type": 'volume',
                #     "viewer": self.volume_viewer
                # }]
        return None

    @debounce(0.3)
    async def delayed_render(self, viewer_arr):
        logger.debug('发送延迟渲染图片')
        content = {}
        for viewer_dict in viewer_arr:
            viewer: Viewer = viewer_dict['viewer']
            img_info = viewer.get_recent_img(EnumImgQuality.high.value)
            view_id = viewer_dict['view_id']
            content[view_id] = img_info
        ans = self.pack_http_ans(content)
        send_msg = self.get_transformed_msg(ans)
        await send_message(send_msg)

    def map_view_type_to_viewer(self, view_type):
        if view_type == EnumViewType.stack.value:
            return self.stack_viewer
        elif view_type == EnumViewType.Ax.value:
            return self.ax_viewer
        elif view_type == EnumViewType.Sag.value:
            return self.sag_viewer
        elif view_type == EnumViewType.Cor.value:
            return self.cor_viewer
        else:
            return None

    def get_viewer_loader_by_view_id(self, view_type, op_type,view_mode, group_id, view_id):
        ans = None
        match (view_mode, view_type, op_type):
            case (EnumViewMode.quick.value,_, _):
                ans = [
                    {
                        "view_type": view_type,
                        "viewer": self.map_view_type_to_viewer(view_type),
                        "view_id": view_id,
                        "group_id": group_id
                    }
                ]
            case (_, _, EnumOpType.pan.value):
                ans = [
                    {
                        "view_type": view_type,
                        "viewer": self.map_view_type_to_viewer(view_type),
                        "view_id": view_id,
                        "group_id": group_id
                    }
                ]
            case (_,EnumViewType.stack.value,_):
                ans = [
                    {
                        "view_type": view_type,
                        "viewer": self.map_view_type_to_viewer(view_type),
                        "view_id": view_id,
                        "group_id": group_id
                    }
                ]
            case (_,_, EnumOpType.set_mip.value):
                ax_view_id = ViewGroupManager.group_id_dict[group_id][EnumViewType.Ax.value]
                sag_view_id = ViewGroupManager.group_id_dict[group_id][EnumViewType.Sag.value]
                cor_view_id = ViewGroupManager.group_id_dict[group_id][EnumViewType.Cor.value]
                ans = [
                    {
                        "view_type": EnumViewType.Ax.value,
                        "viewer": self.map_view_type_to_viewer(EnumViewType.Ax.value),
                        "view_id": ax_view_id,
                        "group_id": group_id
                    },
                    {
                        "view_type": EnumViewType.Sag.value,
                        "viewer": self.map_view_type_to_viewer(EnumViewType.Sag.value),
                        "view_id": sag_view_id,
                        "group_id": group_id
                    },
                    {
                        "view_type": EnumViewType.Cor.value,
                        "viewer": self.map_view_type_to_viewer(EnumViewType.Cor.value),
                        "view_id": cor_view_id,
                        "group_id": group_id
                    },
                ]
            case (_,EnumViewType.Ax.value | EnumViewType.Sag.value | EnumViewType.Cor.value , _):
                ax_view_id =  ViewGroupManager.group_id_dict[group_id][EnumViewType.Ax.value]
                sag_view_id = ViewGroupManager.group_id_dict[group_id][EnumViewType.Sag.value]
                cor_view_id = ViewGroupManager.group_id_dict[group_id][EnumViewType.Cor.value]
                ans = [
                    {
                        "view_type": EnumViewType.Ax.value,
                        "viewer": self.map_view_type_to_viewer(EnumViewType.Ax.value),
                        "view_id": ax_view_id,
                        "group_id": group_id
                    },
                    {
                        "view_type": EnumViewType.Sag.value,
                        "viewer": self.map_view_type_to_viewer(EnumViewType.Sag.value),
                        "view_id": sag_view_id,
                        "group_id": group_id
                    },
                    {
                        "view_type": EnumViewType.Cor.value,
                        "viewer": self.map_view_type_to_viewer(EnumViewType.Cor.value),
                        "view_id": cor_view_id,
                        "group_id": group_id
                    },
                ]
        return ans


    async def handle_op(self, msg):
        view_type = msg['viewType']
        op_type = msg['opType']
        group_id = msg['groupId']
        op_view_id = msg['viewId']
        view_mode =msg['viewMode']
        viewer_arr = self.get_viewer_loader_by_view_id(view_type, op_type,view_mode, group_id, op_view_id)
        need_send_delay_high_quality_img = True
        content = {}
        no_ans = False
        match op_type:

            case EnumOpType.window.value:
                x = msg['x']
                y = msg['y']
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.update_window(x, y)
                    img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                    content[view_id] = img_info

            case EnumOpType.set_invert.value:
                enabled = msg['value']
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.set_invert(enabled)
                    img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                    content[view_id] = img_info

            case EnumOpType.set_window.value:
                window_width = msg['window_width']
                window_center = msg['window_center']
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.set_window(window_width, window_center)
                    img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                    content[view_id] = img_info

            case EnumOpType.rotate.value:
                rotate_type = msg['subOpType']
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    if rotate_type == EnumRotateSubType.flip_x.value:
                        viewer.update_flip(True, False)
                    elif rotate_type == EnumRotateSubType.flip_y.value:
                        viewer.update_flip(False, True)
                    elif rotate_type == EnumRotateSubType.cw90.value:
                        viewer.update_rotate(rotate_type)
                    elif rotate_type == EnumRotateSubType.ccw90.value:
                        viewer.update_rotate(rotate_type)
                    img_info = viewer.get_recent_img()
                    content[view_id] = img_info

            case EnumOpType.rotate3D.value:
                pass
                # x = msg['x']
                # y = msg['y']
                # for viewer_dict in viewer_arr:
                #     viewer: Viewer = viewer_dict['viewer']
                #     viewer_type = viewer_dict['view_type']
                #     viewer: VolumeLoader
                #     viewer.update_rotate(x, y)
                #     img_info = viewer.get_recent_img()
                #
                #     img_dict.setdefault(viewer_type, img_info)

            case EnumOpType.getPosition.value:
                display_to_render_ratio = self.volume_info.view_info[view_type].display_to_render_ratio
                x = msg['x'] * display_to_render_ratio
                y = msg['y'] * display_to_render_ratio
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.get_current_mouse_position(y, x)
                    img_info = viewer.get_recent_without_img()
                    content[view_id] = img_info
                need_send_delay_high_quality_img = False

            case EnumOpType.zoom.value:
                zoom_factor = msg['zoom']
                for viewer_dict in viewer_arr:
                    viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.update_zoom(zoom_factor)
                    img_info = viewer.get_recent_img()
                    content[view_id] = img_info

            case EnumOpType.pan.value:
                display_to_render_ratio = self.volume_info.view_info[view_type].display_to_render_ratio
                delta_x = round(msg['x'] * display_to_render_ratio, 1)
                delta_y = round(msg['y'] * display_to_render_ratio, 1)
                # print(f'msg_x:{msg['x']}, msg_y:{msg['y']} deta_x, {delta_x}, delta_y:{delta_y}')
                if delta_x == 0 and delta_y == 0:
                    no_ans = True
                    pass
                else:
                    for viewer_dict in viewer_arr:
                        viewer = viewer_dict['viewer']
                        view_id = viewer_dict['view_id']
                        viewer.update_pan(delta_x, delta_y)
                        img_info = viewer.get_recent_img()
                        content[view_id] = img_info

            case EnumOpType.scroll.value:
                scroll = msg['scroll']
                for viewer_dict in viewer_arr:
                    viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    if view_id == op_view_id:
                        viewer.update_current_slice(scroll)
                    img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                    content[view_id] = img_info

            case EnumOpType.scrollTo.value:
                scroll_to_index = msg['toIndex']
                for viewer_dict in viewer_arr:
                    viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.slice_to_index(scroll_to_index)
                    img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                    content[view_id] = img_info

            # 移动mpr中的cross
            case EnumOpType.move_scroll_absolute.value:
                display_to_render_ratio = self.volume_info.view_info[view_type].display_to_render_ratio
                x = msg['x'] * display_to_render_ratio
                y = msg['y'] * display_to_render_ratio
                self.volume_info.update_slice_position(view_type, y, x)
                for viewer_dict in viewer_arr:
                    viewer = viewer_dict['viewer']
                    viewer_id = viewer_dict['view_id']
                    img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                    content[viewer_id] = img_info

            case EnumOpType.measure.value:
                display_to_render_ratio = self.volume_info.view_info[view_type].display_to_render_ratio
                sub_op_type = msg['subOpType']
                x = msg['x'] * display_to_render_ratio
                y = msg['y'] * display_to_render_ratio
                action_type = msg['actionType']

                for viewer_dict in viewer_arr:
                    viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    if view_id == op_view_id:
                        viewer.handle_draw(sub_op_type, (x, y), action_type)
                        img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                        content[view_id] = img_info

            case EnumOpType.reset.value:
                reset_type = msg['subOpType']
                match (reset_type, view_type):
                    case ('resetAll', EnumViewType.Ax.value | EnumViewType.Sag.value | EnumViewType.Cor.value | EnumViewType.MPR.value):
                        self.volume_info.reset_mpr_slice()
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    viewer.reset(reset_type)
                    img_info = viewer.get_recent_img()
                    content[view_id] = img_info

            case EnumOpType.get_img.value:
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    img_info = viewer.get_recent_img()
                    content[view_id] = img_info

            case EnumOpType.set_mip.value:
                mip_setting = msg['mip_setting']
                enable_mip = mip_setting['enableMIP']
                mip_mode = mip_setting['mipMode']
                mip_val = mip_setting['mipVal']
                self.volume_info.set_mip(enable_mip, mip_mode, mip_val)
                for viewer_dict in viewer_arr:
                    viewer: Viewer = viewer_dict['viewer']
                    view_id = viewer_dict['view_id']
                    img_info = viewer.get_recent_img()
                    content[view_id] = img_info


            case EnumOpType.play.value:
                play_setting = msg['play_setting']
                play_speed = play_setting['playSpeed']
                view_type = play_setting['viewType']
                play_status = play_setting['playStatus']  # play  end

                if play_status == EnumPlayState.play.value:
                    self.is_playing = True
                elif play_status == EnumPlayState.pause.value:
                    self.is_playing = False

                while True:
                    # start_time = time.time()
                    if not self.is_playing:
                        break
                    content = {}
                    self.volume_info.toggle_next(view_type)

                    for viewer_dict in viewer_arr:
                        viewer: Viewer = viewer_dict['viewer']
                        view_id = viewer_dict['view_id']
                        img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                        content[view_id] = img_info
                    ans = self.pack_http_ans(content)
                    send_msg = self.get_transformed_msg(ans)
                    await send_message(send_msg)
                    # 无GPU情况下约耗时0.013- 0.017秒。
                    # end_time = time.time()
                    # logger.info('发送图片耗时：', end_time - start_time)
                    await asyncio.sleep(play_speed / 1000)
                    no_ans = True


        if not no_ans:
            ans = self.pack_http_ans(content)
            send_msg = self.get_transformed_msg(ans)
            await send_message(send_msg)
            if need_send_delay_high_quality_img:
                await self.delayed_render(viewer_arr)



    async def handle_play(self, play_setting):
        play_speed = play_setting['playSpeed']
        view_type = play_setting['viewType']
        play_status = play_setting['playStatus']  # play  end

        if play_status == EnumPlayState.play.value:
            self.is_playing = True
        elif play_status == EnumPlayState.pause.value:
            self.is_playing = False

        if view_type == EnumViewType.stack.value:
            viewer_arr = self.get_view_type(view_type)
        else:
            viewer_arr = self.get_view_type(EnumViewType.MPR.value)
        while True:
            # start_time = time.time()
            if self.is_playing is False:
                break
            content = {}
            self.volume_info.toggle_next(view_type)

            for viewer_dict in viewer_arr:
                viewer: Viewer = viewer_dict['viewer']
                viewer_type = viewer_dict['view_type']
                img_info = viewer.get_recent_img(EnumImgQuality.low.value)
                content[viewer_type] = img_info
            ans = self.pack_http_ans(view_type, content)
            send_msg = self.get_transformed_msg(ans)
            await send_message(send_msg)
            # 无GPU情况下约耗时0.013- 0.017秒。
            # end_time = time.time()
            # logger.info('发送图片耗时：', end_time - start_time)
            await asyncio.sleep(play_speed / 1000)

